package com.mf.utils.calculate;

import com.mf.vo.BuyAndSellVo;
import com.mf.vo.CounterSum;
import com.mf.vo.KDJVo;
import com.mf.vo.MACD.MACDVo;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: lmf
 * @Create: 2024/5/11 22:43
 * Description:
 */
public class KdjCalculator {
    // 本金
    public static double money = 10000;
    // 止损点
    public static double stopLoss = 0.05;
    // 允许cci值是几天内最低
    public static int cciDays = 4;
    // 买入后几日内阴线卖出
    public static int buyTime = 0;
    // 买入后当前跌幅超过 n% 直接卖出
    public static double buyPrice1 = 0.03;
    // 买入后最大回撤率
    public static double maxLoss = 0.05;
    public static void findIncreasingIndices2(MACDVo macdVo) {
        // cci底部值
        double bottomCci = getBottomCci(macdVo);
        System.out.println(bottomCci+"cci底部");
        // 可以买入日期的下标
        List<BuyAndSellVo> buyAndSellVos = calculateCanBuyIndex(macdVo, 0.0);

        // 成功的次数
        int success = 0;
        // 失败的次数
        int error = 0;

        // 止损次数
        int stopLossCount = 0;
        // cci低于n天后的卖出次数
        int cciCount = 0;
        // kdj死叉卖出次数
        int kdjCount = 0;
        // 花费天数
        int buyTimeCount = 0;
        int profitCount = 0;
        int profitLossCount = 0;
        int getBuyPriceNCount = 0;
        int buyPrice1Count = 0;


        // 根据 buyAndSellVos 中的 盈亏百分和本金计算 剩余本金
        for (BuyAndSellVo buyAndSellVo : buyAndSellVos) {
            money  = money* (1 + buyAndSellVo.getProfit());
            stopLossCount += buyAndSellVo.getStopLossCount();
            cciCount += buyAndSellVo.getCciCount();
            kdjCount += buyAndSellVo.getKdjCount();
            buyTimeCount += buyAndSellVo.getBuyTime();
            profitCount += buyAndSellVo.getProfitCount();
            profitLossCount += buyAndSellVo.getProfitLossCount();
            getBuyPriceNCount+= buyAndSellVo.getBuyPriceNCount();
            buyPrice1Count += buyAndSellVo.getBuyPrice1Count();


            if(buyAndSellVo.getStatus() == 1){
                success++;
            }else {
//                if(buyAndSellVo.getStopLossCount() == 1){
//                    System.out.println("===========================开始========================");
//                    System.out.println(buyAndSellVo.getBuyDate()+"买入日期");
//                    System.out.println("买入价格："+buyAndSellVo.getBuyPrice());
//                    System.out.println(buyAndSellVo.getSellDate()+"卖出日期");
//                    System.out.println("卖出日期："+buyAndSellVo.getSellDate());
//                    System.out.println("卖出价格："+buyAndSellVo.getSellPrice());
//                    System.out.println("卖出盈亏百分比："+buyAndSellVo.getProfit());
//                    System.out.println(money);
//                    System.out.println("===========================结束========================");
//                }


                error++;
            }

            System.out.println("===========================开始========================");
            System.out.println(buyAndSellVo.getBuyDate()+"买入日期");
            System.out.println("买入价格："+buyAndSellVo.getBuyPrice());
            System.out.println(buyAndSellVo.getSellDate()+"卖出日期");
            System.out.println("卖出日期："+buyAndSellVo.getSellDate());
            System.out.println("卖出价格："+buyAndSellVo.getSellPrice());
            System.out.println("卖出盈亏百分比："+buyAndSellVo.getProfit());
            System.out.println(money);
            System.out.println("===========================结束========================");

        }
        System.out.println("剩余本金："+money);
        System.out.println("成功次数："+success);
        System.out.println("失败次数："+error);
        System.out.println("成功率：" + success * 1.0 / (success + error));
        System.out.println("失败率"+ error * 1.0 / (success + error));
        System.out.println("止损次数"+stopLossCount);
        System.out.println("cci次数止损次数"+cciCount);
        System.out.println("kdj次数止损次数"+kdjCount);
        System.out.println("止盈次数"+profitCount);
        System.out.println("当天损失超过n%卖出次数"+profitLossCount);
        System.out.println("买入价格n天后阴线卖出次数"+getBuyPriceNCount);
        System.out.println("花费天数"+buyTimeCount);
        System.out.println("买入后超跌卖出次数"+buyPrice1Count);


    }


    // 获取cci底部值
    private static double getBottomCci(MACDVo macdVo){
        // cci指标
        List<Double> cci = macdVo.getCci();
        double sumCci = 0;
        for (Double aDouble : cci) {
            sumCci += aDouble;
        }
        return  sumCci / cci.size();
    }

    // 计算可以买入的下标
    private static List<BuyAndSellVo> calculateCanBuyIndex(MACDVo macdVo, double bottomCci) {
        // cci指标
        List<Double> cci = macdVo.getCci();
        // k线指标
        double[][] k = macdVo.getK();
        // kdj 指标
        List<KDJVo> kdj = macdVo.getKdj();
        List<BuyAndSellVo> buyAndSellVos = new ArrayList<>();

        // k[0] 开盘价   k[1] 收盘价  k[2] 最低价  k[3] 最高价
        for(int i = 0; i < cci.size(); i++){
                Integer canBuyK = findCanBuyK(macdVo, i);
                if(canBuyK != -1){
                    final CounterSum counterSum = new CounterSum();

                    // 买入价格 当天收盘收盘价买入
                    double buyPrice = k[canBuyK][1];
                    // 最高价
                    double maxPrice = 0;
                    // 止盈价
                    double profitPrice = 0;

                    // 对比买入后3天内如果有收盘价小于开盘价，后收盘价直接卖出
                    for (int j = canBuyK+1; j < cci.size(); j++){
                        // 查看kdj是否死叉
                        // 查询前一日kdj j线是否大于k线
                        boolean a = kdj.get(j - 1).getK() < kdj.get(j - 1).getJ();
                        // 如果kdj j线小于或者等于k线，卖出
                        boolean a1 = kdj.get(j).getJ()  <= kdj.get(j).getK();
                        final double j1 = kdj.get(j).getJ();
                        final double k1 = kdj.get(j).getK();
                        System.out.println(j1);
                        System.out.println(k1);
                        // cci 如果是前cciDays日最低值 计数器
                        int count = 0;
                        // cci 如果是前cciDays日最低值 也直接卖出
                        for(int x = 0; x < cciDays; x++){
                            if(i - x > 0){
                                if(cci.get(j) < cci.get(i - x)){
                                    count++;
                                }
                            }

                        }

                        String date111 = macdVo.getDate()[j];
                        String s = macdVo.getDate()[canBuyK];
                        System.out.println("=====================");
                        System.out.println("卖出日期："+date111);
                        System.out.println("买入日期"+s);
                        if(s.contains("2024-01-24")){
                            System.out.println("==");
                        }
                        System.out.println("=====================");


                        // 如果当天收盘价大于最高价那么最高价等于收盘价
                        if(k[j][0] > maxPrice){
                            maxPrice = k[j][3];
                        }

                        // k[0] 开盘价   k[1] 收盘价  k[2] 最低价  k[3] 最高价


                        // 如果 收盘价 小于最高价 maxLoss 那么卖出
                        if(k[j][1] < maxPrice * (1 - maxLoss)){

                            // 如果最高收益下跌 maxLoss% 直接卖出
                            counterSum.setBuyPrice1Count(counterSum.getBuyPrice1Count() + 1);
                            buyAndSellVos.add(insertBuyAndSellVo(macdVo, canBuyK, j, counterSum, i));
                            i = j;
                            break;
                        }else
                        if(k[j][0] > k[j][1] * (1 + buyPrice1)){
                            // 如果当天下跌 buyPrice1% 直接卖出
                            counterSum.setProfitLossCount(counterSum.getProfitLossCount() + 1);
                            buyAndSellVos.add(insertBuyAndSellVo(macdVo, canBuyK, j, counterSum, i));
                            i = j;
                            break;
                        } else
                        if(k[j][2] < buyPrice * (1 - stopLoss)){



                            // 如果价格跌破止损点，卖出
                            counterSum.setStopLossCount(counterSum.getStopLossCount() + 1);
                            buyAndSellVos.add(insertBuyAndSellVo(macdVo, canBuyK, j,counterSum,i));
                            i = j;
                            break;
                        }else
                            // ---------------加上条件后损失变大，，后续看下是不是个例--------------------------
                            if(j <= canBuyK + buyTime){
                                System.out.println(canBuyK + buyTime);
                                System.out.println(j);
                                System.out.println(k[j][1]);
                                System.out.println(buyPrice);
                                // 如果买入后 buyTime 天有阴线直接卖出
                                if(k[j][1] <= k[j][0]){

                                    // 如果买入前 buyTime 天有阴线直接卖出
                                    counterSum.setBuyPriceNCount(counterSum.getBuyPriceNCount() + 1);
                                    buyAndSellVos.add(insertBuyAndSellVo(macdVo, canBuyK, j,counterSum,i));
                                    i = j;
                                    break;
                                }

                            }else
                            if (cciDays == count) {
                                // cci 如果是前 cciDays 日最低值 也直接卖出
                                counterSum.setCciCount(counterSum.getCciCount() + 1);
                                buyAndSellVos.add(insertBuyAndSellVo(macdVo, canBuyK, j,counterSum,i));
                                i = j;
                                break;
                            }else if(a && a1){

                                counterSum.setKdjCount(counterSum.getKdjCount() + 1);
                                // kdj死叉卖出

                                buyAndSellVos.add(insertBuyAndSellVo(macdVo, canBuyK, j,counterSum,i));
                                i = j;
                                break;
                            }
//                         if(profitPrice > 0 && k[j][1] < buyPrice * (1 + 0.005)){
//                            System.out.println(buyPrice);
//                            System.out.println(k[j][1]);
//                            System.out.println(macdVo.getDate()[j]+"==============================止盈");
//                            System.out.println("买入日期"+macdVo.getDate()[canBuyK]);
//                            // 对于已经盈利的但是后面几天股票又下降的设置止盈价
//                            counterSum.setProfitCount(counterSum.getProfitCount() + 1);
//                            buyAndSellVos.add(insertBuyAndSellVo(macdVo, canBuyK, j, counterSum, i));
//                            i = j;
//                            break;
//                        }

                        // 设置止盈价
//                        if(k[j][1] > buyPrice * (1 * 0.1)){
//                            profitPrice = k[j][1];
//                        }
                    }
                }
        }

        return  buyAndSellVos;
    }


    /**
     * @param macdVo macedVo
     * @param BuyI 买入下标
     * @param SellI 卖出下标
     * @return 插入买入卖出buyAndSellVo 类数据
     */
    public static BuyAndSellVo insertBuyAndSellVo(MACDVo macdVo, Integer BuyI,
                                                  Integer SellI,CounterSum counterSum, Integer i) {
        // k[0] 开盘价   k[1] 收盘价  k[2] 最低价  k[3] 最高价
        // k线指标
        double[][] k = macdVo.getK();
        // 日期
        String[] date = macdVo.getDate();
        // 股票代码
        String dm = macdVo.getDm();
        BuyAndSellVo buyAndSellVo = new BuyAndSellVo();
        // 股票代码
        buyAndSellVo.setDm(dm);
        // 买入日期
        buyAndSellVo.setBuyDate(date[BuyI]);
        // 买入价格
        buyAndSellVo.setBuyPrice(k[BuyI][1]);
        buyAndSellVo.setBuyCci(macdVo.getCci().get(i));
        // 卖出日期
        buyAndSellVo.setSellDate(date[SellI]);
        // 卖出价格
        buyAndSellVo.setSellPrice(k[SellI][1]);
        // 花费时间
        buyAndSellVo.setBuyTime(SellI - BuyI);
        // 亏损还是盈利 卖出价格大于买入价格就盈利 设置为 1 ，否则为 0
        int profit =k[SellI][0] > k[BuyI][1] ? 1 : 0 ;
        buyAndSellVo.setStatus(profit);
        // 盈亏百分比 卖出价格和买入价格的差值换算成百分比
        double profitPercentage = ((k[SellI][1] - k[BuyI][1]) / k[SellI][0]);
        buyAndSellVo.setProfit(profitPercentage);
        buyAndSellVo.setStopLossCount(counterSum.getStopLossCount());
        buyAndSellVo.setCciCount(counterSum.getCciCount());
        buyAndSellVo.setKdjCount(counterSum.getKdjCount());
        buyAndSellVo.setProfitCount(counterSum.getProfitCount());
        buyAndSellVo.setStopLossCount(counterSum.getStopLossCount());

        // getBuyPriceNCount
        buyAndSellVo.setBuyPriceNCount(counterSum.getBuyPriceNCount());
        // buyPrice1Count
        buyAndSellVo.setBuyPrice1Count(counterSum.getBuyPrice1Count());
        // profitLossCount
        buyAndSellVo.setProfitLossCount(counterSum.getProfitLossCount());

        return  buyAndSellVo;

    }

    // 查找可买入的k线日期
    private static Integer findCanBuyK(MACDVo macdVo, Integer i) {
        // 边界条件：如果i小于0，说明已经超出了数组范围，没有找到符合条件的元素
        if (i < 0 || i - 1 < 0) {
            return -1; // 或者返回-1，或者其他表示未找到的值
        }
        final String s = macdVo.getDate()[i];
        if("2022-07-08".equals(s)){
            System.out.println();
        }
        // kdj指标
        final List<KDJVo> kdj = macdVo.getKdj();
        // k线指标
        double[][] k = macdVo.getK();
        // k[0] 开盘价   k[1] 收盘价  k[2] 最低价  k[3] 最高价
        //  当天的最低价大于前一天的最低价
        boolean flag1 = k[i][2] > k[i - 1][2];

        // kdj 指标金叉
        boolean flag2 = false;

        // cci指标
        List<Double> cci = macdVo.getCci();
        boolean flag3 = false;
        if(i - 2 >= 0){
            final Double v = cci.get(i);
            final Double v1 = cci.get(i - 1)+5;
            final Double v2 = cci.get(i - 2)+ 3;
            System.out.println(v+"="+v1+"--"+v2);
            flag3 =  v > v1  && v > v2 ;


            // kdj死叉
            // 前一天J值大于前一天K值
            boolean a1 = kdj.get(i - 1).getK() > kdj.get(i - 1).getJ();
            // 当天J值大于当天K值
            boolean a2= kdj.get(i).getK() < kdj.get(i).getJ() +0.2;
            // 当天k线开盘价和收盘价不能大于5%
            boolean a = (k[i][1] - k[i][0]) / k[i][1] < 0.05;
            //当天收盘价与前一天收盘价对比不能大于8%
            boolean a4 = (k[i][1] - k[i - 1][1]) / k[i][1] < 0.08;
            final double j = kdj.get(i).getJ();
            System.out.println(j);
            // 当天J值小于40
            boolean a5 = kdj.get(i).getK() < 40 ;
            if (a1 && a2 && a && a4 && a5) {
                flag2 = true;
            }
        }


        if ( flag2 && flag1 && flag3){
            return  i;
        }else{
            return  -1;
        }
    }

}
